package com.seezoon.domain.service.user;

import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import com.seezoon.domain.service.user.vo.CaptchaVO;
import com.seezoon.infrastructure.error.ErrorCode;
import com.seezoon.infrastructure.exception.ExceptionFactory;
import com.seezoon.infrastructure.properties.AppProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import java.awt.image.BufferedImage;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.time.Instant;
import java.util.Date;
import java.util.Properties;
import java.util.UUID;
import javax.imageio.ImageIO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

/**
 * 验证码服务
 */
@Slf4j
@Service
@Validated
public class CaptchaService {

    private static final String TYPE = "type";
    private static final String CODE_HASH = "codeHash";
    private final Key signKey;
    private final JwtParser jwtParser;
    private final Producer producer;

    public CaptchaService(AppProperties appProperties) {
        String secretKey = appProperties.getSecretKey();
        this.signKey = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
        this.producer = createDefaultKaptcha();
        this.jwtParser = Jwts.parser().setSigningKey(signKey).build();

    }

    /**
     * @param target 验证码目标
     * @param type 类型，如登录，注册等
     * @param expireSeconds 多少秒后过期
     * @return
     */
    public CaptchaVO generate(@NotEmpty String target, @NotEmpty String type, @Min(1) long expireSeconds) {
        // 生成验证码文本
        String capText = producer.createText();
        String id = UUID.randomUUID().toString();
        String compacted = Jwts.builder()
                .id(id)
                .setSubject(target)
                .claim(TYPE, type)
                .claim(CODE_HASH, DigestUtils.sha256Hex(capText))
                .setIssuedAt(Date.from(Instant.now()))
                .setExpiration(Date.from(Instant.now().plusSeconds(expireSeconds)))
                .signWith(SignatureAlgorithm.HS256, signKey).compact();

        // 生成验证码图片
        BufferedImage bi = producer.createImage(capText);

        // 转换为Base64
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            ImageIO.write(bi, "jpg", baos);
            byte[] bytes = baos.toByteArray();
            String base64Image = Base64.encodeBase64String(bytes);
            base64Image = "data:image/jpeg;base64," + base64Image;
            CaptchaVO captchaVO = new CaptchaVO(id, capText, base64Image, compacted);
            return captchaVO;
        } catch (Exception e) {
            log.error("generate captcha error", e);
            throw ExceptionFactory.bizException(ErrorCode.GENERATE_CAPTCHA_ERROR);
        }
    }

    public boolean verify(@NotEmpty String target, @NotEmpty String type, @NotEmpty String captchaText,
            @NotEmpty String captchaToken) {
        // 过期、格式错误、签名错误都是有异常 JwtException
        Jws<Claims> claimsJws = null;
        try {
            claimsJws = jwtParser.parseSignedClaims(captchaToken);
        } catch (JwtException e) {
            log.error("jwt captcha parse error {}", e.getMessage());
            return false;
        }
        Claims payload = claimsJws.getPayload();
        // 如果安全要求更高，可以记录到DB,标识已用过，然后定时清理过期的
        String id = payload.getId();
        String payloadType = payload.get(TYPE, String.class);
        String subject = payload.getSubject();
        String hash = payload.get(CODE_HASH, String.class);
        if (!type.equals(payloadType)) {
            log.error("captcha type not match actual type:{},expected:{}", payloadType, type);
            return false;
        }
        if (!target.equals(subject)) {
            log.error("captcha target not match actual target:{},expected:{}", subject, target);
            return false;
        }
        String expectedHash = DigestUtils.sha256Hex(captchaText);
        if (!expectedHash.equals(hash)) {
            log.error("captcha text {} not match actual target:{},expected:{}", captchaText, hash, expectedHash);
            return false;
        }
        return true;
    }

    private Producer createDefaultKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 1. 基础尺寸 - 增大验证码区域
        properties.setProperty("kaptcha.image.width", "160");
        properties.setProperty("kaptcha.image.height", "60");

        // 2. 字符设置 - 增强复杂性
        properties.setProperty("kaptcha.textproducer.char.length", "4"); // 增加长度
        properties.setProperty("kaptcha.textproducer.char.string", "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"); // 排除易混淆字符
        properties.setProperty("kaptcha.textproducer.char.space", "4"); // 字符间距

        // 3. 字体与颜色 - 模仿涂鸦风格
        properties.setProperty("kaptcha.textproducer.font.names", "Arial Black, Impact, Haettenschweiler");
        properties.setProperty("kaptcha.textproducer.font.size", "32");
        properties.setProperty("kaptcha.textproducer.font.color", "0,0,0"); // 纯黑

        // 4. 背景设置 - 强化对比度
        properties.setProperty("kaptcha.background.impl", "com.google.code.kaptcha.impl.DefaultBackground");
        properties.setProperty("kaptcha.background.clear.from", "255,255,255"); // 纯白背景
        properties.setProperty("kaptcha.background.clear.to", "255,255,255");
        properties.setProperty("kaptcha.border", "no"); // 无边框

        // 5. 高级干扰配置 - 模拟图片效果
        // 斜线干扰（使用标准实现）
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
        properties.setProperty("kaptcha.noise.color", "0,0,0"); // 黑色干扰线
        properties.setProperty("kaptcha.noise.line.num", "2"); // 2条干扰线

        // 6. 字符变形 - 增加OCR识别难度
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
        properties.setProperty("kaptcha.obscurificator.gimpy.impl", "com.google.code.kaptcha.impl.DoubleWarp");

        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}
